/**
**************************************************************************************
*Founder Effect                                                                      *
*Copyright (C) 2006-2007, 5d Studios (www.5d-Studios.com)                            *
*                                                                                    *
*This library is free software; you can redistribute it and/or                       *
*modify it under the terms of the GNU Lesser General Public                          *
*License as published by the Free Software Foundation; either                        *
*version 2.1 of the License, or (at your option) any later version.                  *
*                                                                                    *
*This library is distributed in the hope that it will be useful,                     *
*but WITHOUT ANY WARRANTY; without even the implied warranty of                      *
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU                   *
*Lesser General Public License for more details.                                     *
*                                                                                    *
*You should have received a copy of the GNU Lesser General Public                    *
*License along with this library; if not, write to the Free Software                 *
*Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA      *
**************************************************************************************
*/
#include "StdAfx.h"
#include "SDL.h"

#include "Input.h"
#include "../shared/StringUtil.h"
#include "../Kernel.h"

extern Kernel* g_kernel;

/**
=============================
Constructor -
=============================
*/
Input::Input(void)
{
    m_event = new SDL_Event();
    m_current = 0;

    // Temp add buffers from source
    m_currentBuffer = NULL;

    m_joystick = NULL;
    for ( int i = 0; i < MAX_KEYS; i++ ) {
        key[i].isDown   = false;
        key[i].Released = false;
        key[i].TimeStamp    = 0;
        key[i].NextTime     = 0;
    }
    for ( int i = 0; i < MAX_BUTTONS; i++ )
        button[i] = false;

    // clear the joystick buttons
    for ( int i = 0; i < MAX_JOYSTICKBUTTONS; i++ ) {
        joyStickButtons[i].isDown   = false;
        joyStickButtons[i].Released = false;
        joyStickButtons[i].TimeStamp    = 0;
        joyStickButtons[i].NextTime     = 0;
    }
}

/**
=============================
Init this subsystem
=============================
*/
int Input::Init()
{
    g_kernel->GetConsole()->AddCommand( &bind_f, "bind");
    g_kernel->GetConsole()->AddCommand( &unbind_f, "unbind");
    g_kernel->GetConsole()->AddCommand( &unbindAll_f, "unbindall");

    g_kernel->LPrintf("--Input System Online!");
    return 0;
}

/**
=============================
Poll for events from the user
=============================
*/
void Input::Update(float dt)
{
    while( SDL_PollEvent( m_event ) )
    {
        // Handle input
        switch( m_event->type )
        {
            /*-------------------------------------------------------------------
                Keyboard Events
            --------------------------------------------------------------------*/
        case SDL_KEYDOWN:
            // if we have a buffer open, write to it
            if ( m_currentBuffer && m_currentBuffer->IsActive() )
                m_currentBuffer->ProcessKey( true, m_event->key.keysym.sym );
            else
            {
                // check to see if this is a binded command
                GetBinder()->Exec( m_event->key.keysym.sym );
            }
          
            // make sure the key is in bounds
            if ( m_event->key.keysym.sym >= 0 && m_event->key.keysym.sym < MAX_KEYS ) {
                
                // check if this is a newly pressed key
                if ( key[ m_event->key.keysym.sym ].TimeStamp == 0 )
                {
                    key[ m_event->key.keysym.sym ].isDown = true;
                    key[ m_event->key.keysym.sym ].Released = false;
                    // set the time stamps
                    key[ m_event->key.keysym.sym ].TimeStamp = g_kernel->GetTime();
                    key[ m_event->key.keysym.sym ].NextTime = g_kernel->GetTime() + NEXT_TIME;
                }
                // else this key was pressed before, make sure it is within the 
                // next time stamp
                else if ( key[ m_event->key.keysym.sym ].NextTime <= g_kernel->GetTime() )
                {
                    key[ m_event->key.keysym.sym ].isDown = true;
                    key[ m_event->key.keysym.sym ].Released = false;
                    key[ m_event->key.keysym.sym ].TimeStamp = g_kernel->GetTime();
                    key[ m_event->key.keysym.sym ].NextTime = g_kernel->GetTime() + NEXT_TIME;
                }
            }
            if ( m_event->button.button  >= 0 && m_event->button.button  < MAX_BUTTONS )
                button[ m_event->button.button ] = true;

            if ( m_event->jbutton.button >= 0 && m_event->jbutton.button < MAX_JOYSTICKBUTTONS )
            {
                joyStickButtons[ m_event->jbutton.button ].isDown = true;
            }
            break;
        case SDL_KEYUP:
            // clear the attributes
            if ( m_event->key.keysym.sym >= 0 && m_event->key.keysym.sym < MAX_KEYS ) {
                key[ m_event->key.keysym.sym ].isDown = false;
                key[ m_event->key.keysym.sym ].Released = true;
                key[ m_event->key.keysym.sym ].TimeStamp = 0;
                key[ m_event->key.keysym.sym ].NextTime  = 0;
            }
            if ( m_event->button.button  >= 0 && m_event->button.button  < MAX_BUTTONS )
                button[ m_event->button.button ] = false;
            break;

            /*-------------------------------------------------------------------
                Mouse Events
            --------------------------------------------------------------------*/
        case SDL_MOUSEBUTTONDOWN:
            if ( m_event->button.button  >= 0 && m_event->button.button  < MAX_BUTTONS )
            {
                button[ m_event->button.button ] = true;
            }
            break;
        case SDL_MOUSEBUTTONUP:
            if ( m_event->button.button  >= 0 && m_event->button.button  < MAX_BUTTONS )
            {
                button[ m_event->button.button ] = false;
            }
            break;


            /*-------------------------------------------------------------------
                Joystick Events
            --------------------------------------------------------------------*/
        case SDL_JOYBUTTONDOWN:
            if ( m_event->jbutton.button >= 0 && m_event->jbutton.button < MAX_JOYSTICKBUTTONS )
            {
                joyStickButtons[ m_event->jbutton.button ].isDown = true;
            }
            break;
        case SDL_JOYBUTTONUP:
            if ( m_event->jbutton.button >= 0 && m_event->jbutton.button < MAX_JOYSTICKBUTTONS )
            {
                joyStickButtons[ m_event->jbutton.button ].isDown = false;
            }
            break;
        case SDL_QUIT:
            // savely exit the game
            g_kernel->KillAll();
            break;
        case SDL_MOUSEMOTION:
            m_oldx = m_x;
            m_oldy = m_y;
            // Get the new x & y
            m_x = m_event->motion.x;
            m_y = m_event->motion.y;
            break;
        default:
            // ignore
            break;
        }
    }

}
/**
=============================
Shutdown this subsystem
=============================
*/
void Input::Shutdown()
{
    if ( JoystickEnabled() )
        SDL_JoystickClose(m_joystick);

    g_kernel->GetConsole()->RemoveCommand("bind");
    g_kernel->GetConsole()->RemoveCommand("unbind");
    g_kernel->GetConsole()->RemoveCommand("unbindall");
}

/*------------------------------------------------------------------------------
    Joystick operations
------------------------------------------------------------------------------*/

/**
============================
Enable the joystick
============================
*/
void Input::EnableJoystick()
{
    // Poll for joystick events
    if ( !CheckJoystick() )
    {
        m_joystickEnabled = true;
        SDL_JoystickEventState( SDL_ENABLE );
    }
    else
        m_joystickEnabled = false;

}

/**
==========================
check availibility of a joystick
==========================
*/
int Input::CheckJoystick()
{
    /* Initialize the joystick subsystem */
    SDL_InitSubSystem(SDL_INIT_JOYSTICK);
    
    if ( SDL_NumJoysticks() )
    {
        g_kernel->LPrintf( "--Joystick Availible!");
        std::string modelName = SDL_JoystickName(0);
        g_kernel->LPrintf( ("--Model: " + modelName).c_str()  );
        
        // Open joystick
        if(!SDL_JoystickOpened(0))
        {
            m_joystick = SDL_JoystickOpen(0);
            if ( m_joystick )
            {
                g_kernel->LPrintf( "--Number of Axes: %d", SDL_JoystickNumAxes(m_joystick));
                g_kernel->LPrintf( "--Number of Buttons: %d", SDL_JoystickNumButtons(m_joystick));
                g_kernel->LPrintf( "--Number of Balls: %d", SDL_JoystickNumBalls(m_joystick));
            }
        }
        return 0;
    } 
    else
    {
       g_kernel->LPrintf( "--No Joystick Availible!");
    }
    return 1;
}

/**
=========================
Get the X axis motion
=========================
*/
int Input::GetJoystickX()
{
    if ( JoystickEnabled() )
    {
        return SDL_JoystickGetAxis( m_joystick, 0 );
    }
    return 0;
}

/**
=========================
Get the Y axis motion
=========================
*/
int Input::GetJoystickY()
{
    if ( JoystickEnabled() )
    {
        return SDL_JoystickGetAxis( m_joystick, 1 );
    }
    return 0;
}

/**
=========================
Check for a button mash
=========================
*/
int Input::IsJoystickButtonDown(int button, bool stamp)
{
    if ( JoystickEnabled() )
    {   
        bool old = joyStickButtons[button].isDown;
        if ( !stamp )
            joyStickButtons[button].isDown = false;
        return old;
    }
    return 0;
}

/**
=========================
Get the current mouse x coord
=========================
*/
int Input::GetMouseX()
{
    return m_x;
}

/**
=========================
Get the current mouse y coord
=========================
*/
int Input::GetMouseY()
{
    return m_y; 
}
/**
=========================
Get the last x coord of the mouse
=========================
*/
int Input::GetDeltaMX()
{
    return m_oldx;
}
/**
=========================
Get the last y coord of the mouse
=========================
*/
int Input::GetDeltaMY()
{
    return m_oldy;
}
/**
=========================
Check to see if a key is down
========================= 
*/
bool Input::IsKeyDown( int key_, bool stamp  )
{

    bool old = key[key_].isDown;
    if ( !stamp )
        key[key_].isDown = false;
    return old;
}
/**
=========================
Check to see if a button is down
=========================
*/
bool Input::IsButtonDown( int button_, bool stamp  )
{
    if ( !stamp && (m_event->type == SDL_MOUSEBUTTONDOWN && m_event->button.button == button_) )
    {
        bool old = button[button_];
        button[button_] = false;
        return (old) ? true : false;
    }
    return (m_event->type == SDL_MOUSEBUTTONDOWN && m_event->button.button == button_);
}
/**
=========================
Check to see if a key was released
=========================
*/
bool Input::KeyReleased( int key_ )
{
    return m_event->type == SDL_KEYUP && m_event->key.keysym.sym == key_;
}
/**
=========================
check to see if a button was released
=========================
*/
bool Input::ButtonReleased( int button_ )
{
    return m_event->type == SDL_MOUSEBUTTONUP && m_event->button.button == button_;
}

/**
=============================
Deconstructor
=============================
*/
Input::~Input(void)
{
    if ( m_event ) {
        delete m_event;
        m_event = NULL;
    }
    // delete the buffers
    std::map<int, InputBuffer* >::iterator it = buffList.begin();
    for(; it != buffList.end(); )
    {
        if ( it->second )
        {
            it->second->Clear();
            delete it->second;
        }
        ++it;
    }

}


/**
============================================================================================
Bind function - bind a key to a console function
============================================================================================
*/
#include "KeyConverter.h"

// Bind_f :: Exec
/** Binds a key to a function */
void Bind_f::Exec(std::string &s) 
{
    std::string skey;
    KeyConverter convert;
    StringUtil token(s);
    
    // first check the first token
    if ( token.HasNext() ) {
        // the first token should be a key
        skey = token.GetNext();
        int key = convert.ToKeyCode( skey );
        
        // test to see if we have a cmd
        if ( token.HasNext() )
        {
            std::string cvar = token.GetNext();
            // test to see if the second token is a cvar
            if ( g_kernel->GetCvars()->IsValid( cvar ) )
            {
                int size = skey.length() + cvar.length();
                int length = s.length() - size;
                // if so, change its value
                g_kernel->GetCvars()->SetCvarValue( cvar, key );        
            } else {
                // else it is a command
                g_kernel->GetInput()->GetBinder()->Bind( key, s.substr(skey.length(), s.length() - skey.length() ) );
            }
        } else {
            g_kernel->LPrintf( "<usage> bind [key] [action]" );
        }
    } 
    else
    {   // TODO - Print out all binds?
        g_kernel->LPrintf( "<usage> bind [key] [action]" );
    }
}


/**
==================================
Unbind a key
==================================
*/
void UnBind_f::Exec( std::string &s )
{
    if ( s == "" )
    {
        g_kernel->LPrintf( "<usage> unbind [key]" );
    } else
    {
        KeyConverter convert;
        g_kernel->GetInput()->GetBinder()->UnBind( convert.ToKeyCode( s ) );
    }
}

/**
==================================
Unbind all keys
==================================
*/
void UnBindAll_f::Exec( std::string &s )
{
    g_kernel->GetInput()->GetBinder()->UnBindAll();
}